import numpy
from findFunction import findElem, findElemInList
from isFunction import IsWall, IsSolvable
from listnode import ListNode
from typing import Optional,Tuple,List

def constructNext(node: ListNode, cost_func, depth_func) -> ListNode:
    origin: ListNode = node
    pre: ListNode = node
    i: numpy.array = node.getVal()
    row, col = findElem(i, 0)
    for dirt in range(4):
        if IsWall(i, dirt, row, col) is not True:
            now = ListNode(parent=origin, val=changeStatus(i, dirt, row, col))
            cost = cost_func(now)
            depth = depth_func(now)
            now.setCost(cost)
            now.setDepth(depth)
            if pre is origin:
                pre.setNext(now)
            else:
                pre.setCousin(now)
            pre = now
    return origin

def changeStatus(val: numpy.array, dirt: int, row: int, col: int) -> numpy.array:
    node = val.copy()
    if dirt == 0:  
        node[row][col], node[row - 1][col] = node[row - 1][col], node[row][col]
    elif dirt == 1:  
        node[row][col], node[row + 1][col] = node[row + 1][col], node[row][col]
    elif dirt == 2: 
        node[row][col], node[row][col - 1] = node[row][col - 1], node[row][col]
    elif dirt == 3: 
        node[row][col], node[row][col + 1] = node[row][col + 1], node[row][col]
    return node

def Astar(origin: ListNode, target: ListNode
           , cons_func, dist_func, find_func, judge_func=None) -> Optional[List[ListNode]]:

    if judge_func is not None and judge_func(origin.getVal(), target.getVal()) is not True:
        print("Not Solvable!")
        exit(1)
    ans = None
    opened, closed = list(), list()
    if origin is not None:
        opened.append(origin)
    while opened:
        now = opened.pop()
        closed.append(now)
        if (now.getVal() == target.getVal()).all():
            ans = now.getRoad()
            break
        
        now = cons_func(now, cost_func=lambda x: dist_func(x.getVal(), target.getVal()),
                         depth_func=lambda x: now.getDepth() + 1)
        next_node = now.getNext()
        
        while next_node is not None:
            m = find_func(next_node, closed)
            n = find_func(next_node, opened)
            if m is not None and next_node.getFn() < closed[m].getFn():
                if n is None:
                    opened.append(next_node)
                else:
                    opened[n] = next_node
                closed[m] = next_node
            if n is not None and next_node.getFn() < opened[n].getFn():
                opened[n] = next_node
            if m is None and n is None:
                opened.append(next_node)
            next_node = next_node.getCousin()
        opened.sort(key=lambda x: x.getFn(), reverse=True)
    return ans

